<!DOCTYPE html>
<html lang="zh-CN">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>PCEService 路径计算系统</title>
	<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
	<style>
		body {
			font-family: 'Arial', sans-serif;
		}
		.node {
			fill: #3B82F6;
			stroke: #1E40AF;
			stroke-width: 2px;
		}
		.link {
			stroke: #64748B;
			stroke-width: 2px;
		}
		.path-link {
			stroke: #EF4444;
			stroke-width: 3px;
			stroke-dasharray: 5,5;
		}
		.node-label {
			font-size: 12px;
			fill: #FFFFFF;
			text-anchor: middle;
			pointer-events: none;
		}
	</style>
</head>
<body class="bg-gray-50 min-h-screen flex flex-col">
	<header class="bg-blue-600 text-white shadow-md">
		<div class="container mx-auto px-4 py-3 flex justify-between items-center">
			<h1 class="text-xl font-bold">PCEService 路径计算系统</h1>
			<nav>
				<ul class="flex space-x-4">
					<li><a href="#" class="hover:underline">首页</a></li>
					<li><a href="#topology" class="hover:underline">网络拓扑</a></li>
					<li><a href="#path-calculation" class="hover:underline">路径计算</a></li>
					<li><a href="#evaluation" class="hover:underline">路径评估</a></li>
				</ul>
			</nav>
		</div>
	</header>

	<main class="flex-grow container mx-auto px-4 py-6">
		<section id="topology" class="mb-10 bg-white rounded-lg shadow-md p-6">
			<h2 class="text-xl font-semibold mb-4 text-gray-800 border-b pb-2">网络拓扑</h2>
			<div class="flex flex-col md:flex-row gap-6">
				<div class="w-full md:w-2/3 bg-gray-100 rounded-lg p-4">
					<svg id="topology-graph" width="100%" height="400" class="border rounded"></svg>
				</div>
				<div class="w-full md:w-1/3 bg-gray-100 rounded-lg p-4">
					<h3 class="font-medium mb-3 text-gray-700">拓扑信息</h3>
					<div class="mb-4">
						<p class="text-sm text-gray-600"><span id="node-count" class="font-semibold">0</span> 个节点</p>
						<p class="text-sm text-gray-600"><span id="link-count" class="font-semibold">0</span> 条链路</p>
					</div>
					<div class="h-40 overflow-y-auto bg-white rounded border p-2 text-xs text-gray-700">
						<div id="topology-info" class="space-y-1"></div>
					</div>
				</div>
			</div>
		</section>

		<section id="path-calculation" class="mb-10 bg-white rounded-lg shadow-md p-6">
			<h2 class="text-xl font-semibold mb-4 text-gray-800 border-b pb-2">路径计算</h2>
			<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
				<div class="md:col-span-1 space-y-4">
					<div>
						<label class="block text-sm font-medium text-gray-700 mb-1">源节点</label>
						<select id="source-node" class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"></select>
					</div>
					<div>
						<label class="block text-sm font-medium text-gray-700 mb-1">目标节点</label>
						<select id="destination-node" class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"></select>
					</div>
					<div>
						<label class="block text-sm font-medium text-gray-700 mb-1">计算策略</label>
						<select id="path-strategy" class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500">
							<option value="min-delay">最小时延</option>
							<option value="min-hop">最小跳数</option>
							<option value="min-cost">最小成本</option>
							<option value="balanced-bandwidth">带宽均衡</option>
						</select>
					</div>
					<div class="space-y-2">
						<div class="flex items-center">
							<input type="checkbox" id="must-pass-enabled" class="mr-2">
							<label for="must-pass-enabled" class="text-sm text-gray-700">必经节点</label>
						</div>
						<div id="must-pass-nodes" class="pl-6 hidden">
							<select id="must-pass-node-select" class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"></select>
							<button id="add-must-pass" class="mt-1 text-xs bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600">添加</button>
							<div id="must-pass-list" class="mt-2 text-xs text-gray-600"></div>
						</div>
					</div>
					<div class="space-y-2">
						<div class="flex items-center">
							<input type="checkbox" id="must-not-pass-enabled" class="mr-2">
							<label for="must-not-pass-enabled" class="text-sm text-gray-700">必不经节点</label>
						</div>
						<div id="must-not-pass-nodes" class="pl-6 hidden">
							<select id="must-not-pass-node-select" class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"></select>
							<button id="add-must-not-pass" class="mt-1 text-xs bg-blue-500 text-white px-2 py-1 rounded hover:bg-blue-600">添加</button>
							<div id="must-not-pass-list" class="mt-2 text-xs text-gray-600"></div>
						</div>
					</div>
					<button id="calculate-path" class="mt-4 w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">计算路径</button>
					<button id="calculate-multiple" class="mt-2 w-full bg-green-600 text-white py-2 rounded hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2">计算多条路径</button>
				</div>
				<div class="md:col-span-2 bg-gray-100 rounded-lg p-4">
					<h3 class="font-medium mb-3 text-gray-700">计算结果</h3>
					<div id="path-result" class="bg-white rounded border p-4 h-60 overflow-y-auto text-sm text-gray-700"></div>
				</div>
			</div>
		</section>

		<section id="evaluation" class="mb-10 bg-white rounded-lg shadow-md p-6">
			<h2 class="text-xl font-semibold mb-4 text-gray-800 border-b pb-2">路径评估</h2>
			<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
				<div class="md:col-span-1 space-y-4">
					<div>
						<label class="block text-sm font-medium text-gray-700 mb-1">源节点</label>
						<select id="eval-source-node" class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"></select>
					</div>
					<div>
						<label class="block text-sm font-medium text-gray-700 mb-1">目标节点</label>
						<select id="eval-destination-node" class="w-full border rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"></select>
					</div>
					<div class="space-y-2">
						<label class="block text-sm font-medium text-gray-700 mb-1">评估权重</label>
						<div class="flex items-center">
							<input type="range" id="delay-weight" min="0" max="100" value="40" class="w-full">
							<span id="delay-weight-value" class="ml-2 w-10 text-right text-sm text-gray-600">40%</span>
						</div>
						<div class="flex items-center">
							<input type="range" id="hop-weight" min="0" max="100" value="20" class="w-full">
							<span id="hop-weight-value" class="ml-2 w-10 text-right text-sm text-gray-600">20%</span>
						</div>
						<div class="flex items-center">
							<input type="range" id="cost-weight" min="0" max="100" value="20" class="w-full">
							<span id="cost-weight-value" class="ml-2 w-10 text-right text-sm text-gray-600">20%</span>
						</div>
						<div class="flex items-center">
							<input type="range" id="bandwidth-weight" min="0" max="100" value="20" class="w-full">
							<span id="bandwidth-weight-value" class="ml-2 w-10 text-right text-sm text-gray-600">20%</span>
						</div>
						<div class="text-xs text-gray-500 mt-1"><span id="total-weight">100</span>% 总权重</div>
					</div>
					<button id="calculate-best-path" class="mt-4 w-full bg-purple-600 text-white py-2 rounded hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2">计算最佳路径</button>
				</div>
				<div class="md:col-span-2 bg-gray-100 rounded-lg p-4">
					<h3 class="font-medium mb-3 text-gray-700">评估结果</h3>
					<div id="evaluation-result" class="bg-white rounded border p-4 h-60 overflow-y-auto text-sm text-gray-700"></div>
				</div>
			</div>
		</section>
	</main>

	<footer class="bg-gray-800 text-white py-4">
		<div class="container mx-auto px-4 text-center text-sm">
			<p>PCEService 路径计算系统 &copy; 2023</p>
		</div>
	</footer>

	<script>
		// 简单的前端逻辑
		document.addEventListener('DOMContentLoaded', function() {
			// 示例数据 - 实际应用中应从API获取
			const sampleNodes = [
				{ id: 'node1', name: '节点1' },
				{ id: 'node2', name: '节点2' },
				{ id: 'node3', name: '节点3' },
				{ id: 'node4', name: '节点4' },
				{ id: 'node5', name: '节点5' }
			];

			const sampleLinks = [
				{ id: 'link1', source: 'node1', target: 'node2', delay: 5, metric: 10, bandwidth: 100 },
				{ id: 'link2', source: 'node2', target: 'node3', delay: 3, metric: 5, bandwidth: 200 },
				{ id: 'link3', source: 'node3', target: 'node4', delay: 4, metric: 8, bandwidth: 150 },
				{ id: 'link4', source: 'node1', target: 'node3', delay: 8, metric: 12, bandwidth: 100 },
				{ id: 'link5', source: 'node4', target: 'node5', delay: 2, metric: 3, bandwidth: 300 },
				{ id: 'link6', source: 'node2', target: 'node5', delay: 7, metric: 15, bandwidth: 100 }
			];

			// 初始化节点选择器
			function initNodeSelectors() {
				const selectors = [
					'source-node', 'destination-node',
					'must-pass-node-select', 'must-not-pass-node-select',
					'eval-source-node', 'eval-destination-node'
				];

				selectors.forEach(selectorId => {
					const select = document.getElementById(selectorId);
					if (select) {
						select.innerHTML = '';
						sampleNodes.forEach(node => {
							const option = document.createElement('option');
							option.value = node.id;
							option.textContent = node.name;
							select.appendChild(option);
						});
					}
				});
			}

			// 绘制简单拓扑图
			function drawTopology() {
				const svg = document.getElementById('topology-graph');
				if (!svg) return;

				// 清空SVG
				while (svg.firstChild) {
					svg.removeChild(svg.firstChild);
				}

				// 简单的布局算法
				const width = svg.clientWidth;
				const height = svg.clientHeight;
				const nodeRadius = 20;

				// 节点位置映射
				const nodePositions = {};
				const gridSize = Math.ceil(Math.sqrt(sampleNodes.length));
				const cellWidth = (width - nodeRadius * 2) / (gridSize + 1);
				const cellHeight = (height - nodeRadius * 2) / (gridSize + 1);

				// 分配节点位置
				for (let i = 0; i < sampleNodes.length; i++) {
					const row = Math.floor(i / gridSize);
					const col = i % gridSize;
					nodePositions[sampleNodes[i].id] = {
						x: (col + 1) * cellWidth + nodeRadius,
						y: (row + 1) * cellHeight + nodeRadius
					};
				}

				// 绘制链路
				for (const link of sampleLinks) {
					const sourcePos = nodePositions[link.source];
					const targetPos = nodePositions[link.target];

					if (sourcePos && targetPos) {
						const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
						line.setAttribute('x1', sourcePos.x);
						line.setAttribute('y1', sourcePos.y);
						line.setAttribute('x2', targetPos.x);
						line.setAttribute('y2', targetPos.y);
						line.setAttribute('class', 'link');
						svg.appendChild(line);

						// 链路标签
						const midX = (sourcePos.x + targetPos.x) / 2;
						const midY = (sourcePos.y + targetPos.y) / 2;
						const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
						text.setAttribute('x', midX);
						text.setAttribute('y', midY - 10);
						text.setAttribute('text-anchor', 'middle');
						text.setAttribute('font-size', '10');
						text.setAttribute('fill', '#64748B');
						text.textContent = `延迟: ${link.delay}ms`;
						svg.appendChild(text);
					}
				}

				// 绘制节点
				for (const node of sampleNodes) {
					const pos = nodePositions[node.id];
					if (pos) {
						const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
						circle.setAttribute('cx', pos.x);
						circle.setAttribute('cy', pos.y);
						circle.setAttribute('r', nodeRadius);
						circle.setAttribute('class', 'node');
						svg.appendChild(circle);

						// 节点标签
						const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
						text.setAttribute('x', pos.x);
						text.setAttribute('y', pos.y + 4);
						text.setAttribute('class', 'node-label');
						text.textContent = node.name;
						svg.appendChild(text);
					}
				}

				// 更新拓扑信息
				const nodeCount = document.getElementById('node-count');
				const linkCount = document.getElementById('link-count');
				const topologyInfo = document.getElementById('topology-info');

				if (nodeCount) nodeCount.textContent = sampleNodes.length;
				if (linkCount) linkCount.textContent = sampleLinks.length;

				if (topologyInfo) {
					topologyInfo.innerHTML = '';

					// 添加节点信息
					const nodesHeader = document.createElement('div');
					nodesHeader.className = 'font-semibold';
					nodesHeader.textContent = '节点:';
					topologyInfo.appendChild(nodesHeader);

					sampleNodes.forEach(node => {
						const nodeItem = document.createElement('div');
						nodeItem.textContent = `- ${node.name} (${node.id})`;
						topologyInfo.appendChild(nodeItem);
					});

					// 添加链路信息
					const linksHeader = document.createElement('div');
					linksHeader.className = 'font-semibold mt-2';
					linksHeader.textContent = '链路:';
					topologyInfo.appendChild(linksHeader);

					sampleLinks.forEach(link => {
						const sourceNode = sampleNodes.find(n => n.id === link.source);
						const targetNode = sampleNodes.find(n => n.id === link.target);

						if (sourceNode && targetNode) {
							const linkItem = document.createElement('div');
							linkItem.textContent = `- ${sourceNode.name} -> ${targetNode.name} (延迟: ${link.delay}ms, 成本: ${link.metric})`;
							topologyInfo.appendChild(linkItem);
						}
					});
				}
			}

			// 初始化权重滑动条
			function initWeightSliders() {
				const sliders = [
					{ id: 'delay-weight', valueId: 'delay-weight-value' },
					{ id: 'hop-weight', valueId: 'hop-weight-value' },
					{ id: 'cost-weight', valueId: 'cost-weight-value' },
					{ id: 'bandwidth-weight', valueId: 'bandwidth-weight-value' }
				];

				sliders.forEach(({ id, valueId }) => {
					const slider = document.getElementById(id);
					const value = document.getElementById(valueId);

					if (slider && value) {
						value.textContent = `${slider.value}%`;

						slider.addEventListener('input', function() {
							value.textContent = `${this.value}%`;
							updateTotalWeight();
						});
					}
				});

				function updateTotalWeight() {
					const delay = parseInt(document.getElementById('delay-weight').value) || 0;
					const hop = parseInt(document.getElementById('hop-weight').value) || 0;
					const cost = parseInt(document.getElementById('cost-weight').value) || 0;
					const bandwidth = parseInt(document.getElementById('bandwidth-weight').value) || 0;

					const total = delay + hop + cost + bandwidth;
					const totalWeight = document.getElementById('total-weight');
					if (totalWeight) {
						totalWeight.textContent = total;
					}
				}
			}

			// 初始化必经节点和必不经节点的checkbox
			function initCheckboxes() {
				const mustPassEnabled = document.getElementById('must-pass-enabled');
				const mustPassNodes = document.getElementById('must-pass-nodes');
				const mustNotPassEnabled = document.getElementById('must-not-pass-enabled');
				const mustNotPassNodes = document.getElementById('must-not-pass-nodes');

				if (mustPassEnabled && mustPassNodes) {
					mustPassEnabled.addEventListener('change', function() {
						if (this.checked) {
							mustPassNodes.classList.remove('hidden');
						} else {
							mustPassNodes.classList.add('hidden');
						}
					});
				}

				if (mustNotPassEnabled && mustNotPassNodes) {
					mustNotPassEnabled.addEventListener('change', function() {
						if (this.checked) {
							mustNotPassNodes.classList.remove('hidden');
						} else {
							mustNotPassNodes.classList.add('hidden');
						}
					});
				}
			}

			// 初始化按钮事件
			function initButtons() {
				// 计算路径按钮
				const calculatePathBtn = document.getElementById('calculate-path');
				const pathResult = document.getElementById('path-result');

				if (calculatePathBtn && pathResult) {
					calculatePathBtn.addEventListener('click', function() {
						const source = document.getElementById('source-node').value;
						const destination = document.getElementById('destination-node').value;
						const strategy = document.getElementById('path-strategy').value;

						// 模拟API请求
						pathResult.textContent = '正在计算路径...';

						setTimeout(() => {
							// 模拟结果
							const result = {
								path: {
									id: 'path1',
									nodes: [source, 'node3', destination],
									links: ['link4', 'link3'],
									totalDelay: 12,
									totalHop: 2
								},
								message: '路径计算成功'
							};

							pathResult.textContent = JSON.stringify(result, null, 2);
						}, 1000);
					});
				}

				// 计算多条路径按钮
				const calculateMultipleBtn = document.getElementById('calculate-multiple');

				if (calculateMultipleBtn && pathResult) {
					calculateMultipleBtn.addEventListener('click', function() {
						const source = document.getElementById('source-node').value;
						const destination = document.getElementById('destination-node').value;

						// 模拟API请求
						pathResult.textContent = '正在计算多条路径...';

						setTimeout(() => {
							// 模拟结果
							const results = [
								{
									id: 'path1',
									strategy: '最小时延',
									nodes: [source, 'node3', destination],
									links: ['link4', 'link3'],
									totalDelay: 12
								},
								{
									id: 'path2',
									strategy: '最小跳数',
									nodes: [source, 'node2', destination],
									links: ['link1', 'link6'],
									totalHop: 2
								}
							];

							pathResult.textContent = JSON.stringify(results, null, 2);
						}, 1000);
					});
				}

				// 计算最佳路径按钮
				const calculateBestPathBtn = document.getElementById('calculate-best-path');
				const evaluationResult = document.getElementById('evaluation-result');

				if (calculateBestPathBtn && evaluationResult) {
					calculateBestPathBtn.addEventListener('click', function() {
						const source = document.getElementById('eval-source-node').value;
						const destination = document.getElementById('eval-destination-node').value;
						const delayWeight = parseInt(document.getElementById('delay-weight').value) || 0;
						const hopWeight = parseInt(document.getElementById('hop-weight').value) || 0;
						const costWeight = parseInt(document.getElementById('cost-weight').value) || 0;
						const bandwidthWeight = parseInt(document.getElementById('bandwidth-weight').value) || 0;

						// 模拟API请求
						evaluationResult.textContent = '正在计算最佳路径...';

						setTimeout(() => {
							// 模拟结果
							const result = {
								path: {
									id: 'path1',
									nodes: [source, 'node3', destination],
									links: ['link4', 'link3'],
									totalDelay: 12,
									totalHop: 2,
									totalCost: 20
								},
								evaluation: {
									pathId: 'path1',
									totalDelay: 12,
									totalHop: 2,
									totalCost: 20,
									bandwidthUtilization: 0.3,
									score: 0.85
								},
								message: '最佳路径计算成功'
							};

							evaluationResult.textContent = JSON.stringify(result, null, 2);
						}, 1000);
					});
				}
			}

			// 初始化
			initNodeSelectors();
			initWeightSliders();
			initCheckboxes();
			initButtons();
			drawTopology();

			// 窗口大小改变时重新绘制拓扑图
			window.addEventListener('resize', drawTopology);
		});
	</script>
</body>
</html>